#include <QCoreApplication>
#include <iostream>
#include <thread>
#include <fstream>
#include <Windows.h>
#include <mmdeviceapi.h>
#include <Audioclient.h>
#include <Functiondiscoverykeys_devpkey.h>
#include <opus/opus.h>

template <class T>
inline void SafeRelease(T** ppT) {
    if (*ppT) {
        (*ppT)->Release();
        *ppT = NULL;
    }
}
int main(int argc, char *argv[])
{
    IMMDeviceEnumerator* device_enum = nullptr;
    IMMDevice* capture_device = nullptr;
    IAudioClient* capture_audio_client = nullptr;
    IAudioCaptureClient* iaudio_capture_client = nullptr;
    WAVEFORMATEX* wave_format = nullptr;
    uint32_t frame_nb = 0;
    HANDLE stream_event = CreateEvent(NULL,FALSE,FALSE,NULL);
    QCoreApplication a(argc, argv);

    CoInitializeEx(NULL, COINIT_MULTITHREADED);
    HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator),
                                  NULL,
                                  CLSCTX_ALL,
                                  __uuidof(IMMDeviceEnumerator),
                                  (void**)&device_enum);
    hr = device_enum->GetDefaultAudioEndpoint(eCapture,eConsole,&capture_device);

    //设备id
    LPWSTR wsid = nullptr;
    hr = capture_device->GetId(&wsid);
    std::wcout<<std::wstring(wsid)<<std::endl;
    CoTaskMemFree(wsid);
    //设备名称
    IPropertyStore* proStore = 0;
    hr = capture_device->OpenPropertyStore(STGM_READ, &proStore);
    PROPVARIANT name;
    PropVariantInit(&name);
    hr = proStore->GetValue(PKEY_Device_FriendlyName, &name);
    std::wcout<<std::wstring(name.pwszVal)<<std::endl;
    PropVariantClear(&name);
    SafeRelease(&proStore);

    hr = capture_device->Activate(__uuidof(IAudioClient),
                                  CLSCTX_ALL, NULL,
                                  (void**)&capture_audio_client);
    hr = capture_audio_client->GetMixFormat(&wave_format);
    hr = capture_audio_client->Initialize(AUDCLNT_SHAREMODE_SHARED,
                                          AUDCLNT_STREAMFLAGS_EVENTCALLBACK, 0, 0, wave_format,0);
    hr = capture_audio_client->GetBufferSize(&frame_nb);
    hr = capture_audio_client->GetService(__uuidof(IAudioCaptureClient),
                                          (void**)&iaudio_capture_client);
    capture_audio_client->SetEventHandle(stream_event);
    capture_audio_client->Start();

    std::thread capture_thread([=](){
        std::fstream opus_file("capture.opus",std::ios::out|std::ios::binary|std::ios::app);
        std::fstream raw_file("capture.raw",std::ios::out|std::ios::binary|std::ios::app);
        uint32_t nb_nextpacketsize = 0;
        uint8_t* raw_data = nullptr;
        uint32_t nb_frame = 0;
        ulong flags;
        //opus
        int error = 0;
        OpusEncoder* enc = opus_encoder_create(48000,2,OPUS_APPLICATION_AUDIO,&error);
        OpusDecoder* dec = opus_decoder_create(48000,2,&error);
//        opus_encoder_init(enc,48000,2,OPUS_APPLICATION_AUDIO);
        if (opus_encoder_ctl(enc,OPUS_SET_BITRATE(192000)) != OPUS_OK) std::cout << "OPUS_SET_BITRATE" << std::endl;
        if (opus_encoder_ctl(enc,OPUS_SET_BANDWIDTH(OPUS_BANDWIDTH_FULLBAND)) != OPUS_OK) std::cout << "OPUS_SET_BANDWIDTH" << std::endl;
        if (opus_encoder_ctl(enc,OPUS_SET_SIGNAL(OPUS_SIGNAL_MUSIC)) != OPUS_OK) std::cout << "OPUS_SET_SIGNAL" << std::endl;
        uint8_t* enc_data = new uint8_t[1024*1024];
        uint8_t* dec_data = new uint8_t[1024*1024];
        for (uint32_t i = 0;i < 3000;++i) {
            nb_frame = 0;
            WaitForSingleObject(stream_event,500);
            HRESULT hr = iaudio_capture_client->GetNextPacketSize(&nb_nextpacketsize);
            if (nb_nextpacketsize > 0) {
                iaudio_capture_client->GetBuffer(&raw_data,&nb_frame,&flags,0,0);
                if (flags & AUDCLNT_BUFFERFLAGS_SILENT) {
                    iaudio_capture_client->ReleaseBuffer(nb_frame);
                    std::cout << "silent" << std::endl;
                    continue;
                }
                int len = opus_encode_float(enc,(const float*)raw_data,nb_frame,enc_data,1024*1024);
                if (len > 0) {
                    opus_file.write((const char*)enc_data,len);
                    int frame_size = opus_decode(dec,enc_data,len,(opus_int16*)dec_data,13000,0);
                    if (frame_size > 0) {
                        raw_file.write((const char*)dec_data,frame_size*2*2);
                    }
                }
            }
            iaudio_capture_client->ReleaseBuffer(nb_frame);
        }
        opus_encoder_destroy(enc);
        opus_file.close();
        raw_file.close();
        capture_audio_client->Stop();
    });

    CoUninitialize();
    capture_thread.join();
    return a.exec();
}
